﻿/**
 * @file fwave_pcm.cpp
 * Copyright (c) Gaaagaa. All rights reserved.
 * 
 * @author  : Gaaagaa
 * @date    : 2020-02-16
 * @version : 1.0.0.0
 * @brief   : 实现 WAVE 文件读写 PCM 音频数据格式的工具类。
 */

#include "fwave_pcm.h"

#ifdef _MSC_VER
#define _CRT_SECURE_NO_WARNINGS
#endif // _MSC_VER

#include <stdio.h>
#include <memory.h>
#include <assert.h>

////////////////////////////////////////////////////////////////////////////////

//====================================================================

#ifndef XASSERT
#define XASSERT(x)  assert(x)
#endif // XASSERT

//====================================================================

#ifndef X_BYTE_ORDER
#define X_BYTE_ORDER

/**
 * @brief 用于进行判断主机字节序的联合体。
 * @note
 * 小端：低地址存放低字节，高地址存放高字节；
 * 大端：高地址存放低字节，低地址存放高字节。
 */
static union
{
    x_char_t   xct_order[4];
    x_uint32_t xut_order;
} xbyte_order = { { 'L', '?', '?', 'B' } };

#define X_IS_LITTLE_ENDIAN ('L' == (x_char_t)xbyte_order.xut_order)
#define X_IS_BIG_ENDIAN    ('B' == (x_char_t)xbyte_order.xut_order)

#endif // X_BYTE_ORDER

/**********************************************************/
/**
 * @brief 转换 16 位整数字节序。
 */
static inline x_uint16_t cvt16_order(x_uint16_t xut_value)
{
    return ((xut_value << 8) | (xut_value >> 8));
}

/**********************************************************/
/**
 * @brief 转换 32 位整数字节序。
 */
static inline x_uint32_t cvt32_order(x_uint32_t xut_value)
{
    return (((xut_value             ) << 24) |
            ((xut_value & 0x0000FF00) <<  8) |
            ((xut_value & 0x00FF0000) >>  8) |
            ((xut_value             ) >> 24));
}

//====================================================================

#ifdef __cplusplus
#define X_STRUCT_CONSTRUCTOR(name) \
    name(void) { memset(this, 0, sizeof(struct name)); }
#else // !__cplusplus
#define X_STRUCT_CONSTRUCTOR(name)
#endif // __cplusplus

/**
 * @struct x_wave_node_t
 * @brief  WAV 文件中的节点描述信息。
 */
typedef struct __wave_node__
{
   x_char_t   xct_id[4]; ///< 节点标识 ID
   x_uint32_t xut_size;  ///< 节点内容的字节数（不包含 xct_id 和 xut_size 这8个字节，文件中以 小端模式 存储）

   X_STRUCT_CONSTRUCTOR(__wave_node__)
} x_wave_node_t;

/**
 * @struct x_wave_header_t
 * @brief  WAV 文件头的描述信息。
 * @note { id: "RIFF", size: "存储文件的字节数" }。
 */
typedef struct __wave_header__
{
   x_char_t xct_format[4]; ///< 内容为"WAVE"

   X_STRUCT_CONSTRUCTOR(__wave_header__)
} x_wave_header_t;

/**
 * @struct x_wave_format_t
 * @brief  WAV 文件的数据格式描述信息。
 * @note
 * 1. { id: "fmt ", size: "sizeof(x_wave_format_t)" }。
 * 2. 所有字段在文件中以 小端模式 存储。
 */
typedef struct __wave_format__
{
    x_uint16_t xut_format;   ///< 存储音频文件的编码格式，例如若为PCM则其存储值为1，若为其他非PCM格式的则有一定的压缩。
    x_uint16_t xut_channels; ///< 通道数，单通道(Mono)值为1，双通道(Stereo)值为2，等等
    x_uint32_t xut_samprate; ///< 采样率，如 8000, 22050, 44100, 48000 等
    x_uint32_t xut_avgbps;   ///< 位速（每秒存储的bit数），其值 = xut_samprate * xut_channels * xut_sampbits / 8
    x_uint16_t xut_blkalign; ///< 块对齐大小，其值 = xut_channels * xut_sampbits / 8
    x_uint16_t xut_sampbits; ///< 每个采样点的bit数，一般为 8, 16, 32 等。

    X_STRUCT_CONSTRUCTOR(__wave_format__)
} x_wave_format_t;

#define X_WAVE_NDSIZE   (sizeof(x_wave_node_t))
#define X_WAVE_HDSIZE   (sizeof(x_wave_header_t))
#define X_WAVE_FMTSIZE  (sizeof(x_wave_format_t))
#define X_WAVE_MINSIZE  (3 * X_WAVE_NDSIZE + X_WAVE_HDSIZE + X_WAVE_FMTSIZE)

//====================================================================

#ifdef XHF
#undef XHF
#endif // XHF

#define XHF(handle)   ((FILE *)(handle))

////////////////////////////////////////////////////////////////////////////////
// fwave_rpcm_t

//====================================================================

// 
// fwave_rpcm_t : constructor/destructor
// 

fwave_rpcm_t::fwave_rpcm_t(void)
    : m_xht_file(X_NULL)
    , m_xst_abeg(0)
    , m_xst_alen(0)
{
    memset(&m_fmt_info, 0, sizeof(x_format_t));
}

fwave_rpcm_t::~fwave_rpcm_t(void)
{
    close();
}

//====================================================================

// 
// fwave_rpcm_t : public interfaces
// 

/**********************************************************/
/**
 * @brief 打开文件。
 * 
 * @param [in ] xszt_filename : 目标操作的文件路径名。
 * 
 * @return x_errno_t : 错误码。
 * @retval X_ERR_OK, 操作成功。
 * @retval XERR_HINO(xerr_no)，参看 xerrno_table_t 相关枚举值。
 * @retval XERR_LONO(xerr_no)，所引用的模块返回的错误码值。
 */
x_errno_t fwave_rpcm_t::open(x_cstring_t xszt_filename)
{
    x_errno_t xerr_no = X_ERR_UNKNOW;

    do
    {
        //======================================

        if (X_NULL == xszt_filename)
        {
            xerr_no = XERR_MAKE(X_ERR_FILENAME_NULL, EINVAL);
            break;
        }

        if (is_open())
        {
            close();
        }

        m_xht_file = (x_handle_t)fopen(xszt_filename, "rb");
        if (X_NULL == m_xht_file)
        {
            xerr_no = XERR_MAKE(X_ERR_FOPEN_FAILED, errno);
            break;
        }

        //======================================

        x_size_t      xst_bytes = 0;
        x_wave_node_t xwav_node;

        //======================================
        // 验证文件头

        xst_bytes = fread(&xwav_node, 1, X_WAVE_NDSIZE, XHF(m_xht_file));
        if (xst_bytes < X_WAVE_NDSIZE)
        {
            xerr_no = XERR_MAKE(X_ERR_FREAD_RIFFTAG, 0);
            break;
        }

        if ((xwav_node.xct_id[0] != 'R') ||
            (xwav_node.xct_id[1] != 'I') ||
            (xwav_node.xct_id[2] != 'F') ||
            (xwav_node.xct_id[3] != 'F'))
        {
            xerr_no = XERR_MAKE(X_ERR_CMPID_RIFFTAG, 0);
            break;
        }

        x_wave_header_t xheader;
        xst_bytes = fread(&xheader, 1, X_WAVE_HDSIZE, XHF(m_xht_file));
        if (xst_bytes < X_WAVE_HDSIZE)
        {
            xerr_no = XERR_MAKE(X_ERR_FREAD_WAVETAG, 0);
            break;
        }

        if ((xheader.xct_format[0] != 'W') ||
            (xheader.xct_format[1] != 'A') ||
            (xheader.xct_format[2] != 'V') ||
            (xheader.xct_format[3] != 'E'))
        {
            xerr_no = XERR_MAKE(X_ERR_CMPID_WAVETAG, 0);
            break;
        }

        //======================================

        xerr_no = 0;
        x_bool_t xbt_format = X_FALSE;
        x_bool_t xbt_data   = X_FALSE;

        while (!feof(XHF(m_xht_file)))
        {
            //======================================
            // 读取 节点 信息

            xst_bytes = fread(&xwav_node, 1, X_WAVE_NDSIZE, XHF(m_xht_file));
            if (xst_bytes < X_WAVE_NDSIZE)
            {
                xerr_no = XERR_MAKE(X_ERR_FREAD_WAVEND, 0);
                break;
            }

            if (X_IS_BIG_ENDIAN)
            {
                xwav_node.xut_size = cvt32_order(xwav_node.xut_size);
            }

            //======================================
            // PCM 音频数据格式信息

            if (('f' == xwav_node.xct_id[0]) &&
                ('m' == xwav_node.xct_id[1]) &&
                ('t' == xwav_node.xct_id[2]) &&
                (' ' == xwav_node.xct_id[3]))
            {
                if (xbt_format)
                {
                    xerr_no = XERR_MAKE(X_ERR_FMT_REDUNDANT, 0);
                    break;
                }

                if (X_WAVE_FMTSIZE != xwav_node.xut_size)
                {
                    xerr_no = XERR_MAKE(X_ERR_FMT_CTXTSIZE, 0);
                    break;
                }

                x_wave_format_t xformat;
                xst_bytes = fread(&xformat, 1, X_WAVE_FMTSIZE, XHF(m_xht_file));
                if (xst_bytes < X_WAVE_FMTSIZE)
                {
                    xerr_no = XERR_MAKE(X_ERR_FMT_FREADNODE, 0);
                    break;
                }

                if (X_IS_BIG_ENDIAN)
                {
                    xformat.xut_format   = cvt16_order(xformat.xut_format  );
                    xformat.xut_channels = cvt16_order(xformat.xut_channels);
                    xformat.xut_samprate = cvt32_order(xformat.xut_samprate);
                    xformat.xut_avgbps   = cvt32_order(xformat.xut_avgbps  );
                    xformat.xut_blkalign = cvt16_order(xformat.xut_blkalign);
                    xformat.xut_sampbits = cvt16_order(xformat.xut_sampbits);
                }

                if ((xformat.xut_format   != 1) ||
                    (xformat.xut_samprate <= 0) ||
                    (xformat.xut_sampbits <= 0) ||
                    (xformat.xut_channels <= 0) ||
                    (0 != (xformat.xut_sampbits % 8)))
                {
                    xerr_no = XERR_MAKE(X_ERR_FMT_CONTEXT, 0);
                    break;
                }

                // 保存读到的 PCM 格式信息
                m_fmt_info.xut_samprate= xformat.xut_samprate;
                m_fmt_info.xut_sampbits = xformat.xut_sampbits;
                m_fmt_info.xut_channels = xformat.xut_channels;

                xbt_format = X_TRUE;
                continue;
            }

            //======================================
            // PCM 音频数据段信息

            if (('d' == xwav_node.xct_id[0]) &&
                ('a' == xwav_node.xct_id[1]) &&
                ('t' == xwav_node.xct_id[2]) &&
                ('a' == xwav_node.xct_id[3]))
            {
                if (xbt_data)
                {
                    xerr_no = XERR_MAKE(X_ERR_DATA_REDUNDANT, 0);
                    break;
                }

                // 保存数据段的起始位置 以及 长度
                m_xst_abeg = ftell(XHF(m_xht_file));
                m_xst_alen = xwav_node.xut_size;

                xbt_data = X_TRUE;

                // 跳过音频数据存储区
                fseek(XHF(m_xht_file), m_xst_alen, SEEK_CUR);
                continue;
            }

            //======================================

            // 跳过未识别区段
            fseek(XHF(m_xht_file), xwav_node.xut_size, SEEK_CUR);
        }

        if (!xbt_format)
        {
            if (XERR_SUCCEED(xerr_no))
                xerr_no = XERR_MAKE(X_ERR_LACKOF_FMT, 0);
            break;
        }

        if (!xbt_data)
        {
            if (XERR_SUCCEED(xerr_no))
                xerr_no = XERR_MAKE(X_ERR_LACKOF_DATA, 0);
            break;
        }

        // 将文件指针移到音频数据段起始位置
        fseek(XHF(m_xht_file), m_xst_abeg, SEEK_SET);

        //======================================
        xerr_no = 0;
    } while (0);

    if (0 != xerr_no)
    {
        close();
    }

    return xerr_no;
}

/**********************************************************/
/**
 * @brief 关闭文件。
 */
x_void_t fwave_rpcm_t::close(void)
{
    if (X_NULL != m_xht_file)
    {
        fclose(XHF(m_xht_file));
        m_xht_file = X_NULL;
    }

    memset(&m_fmt_info, 0, sizeof(x_format_t));
    m_xst_abeg = 0;
    m_xst_alen = 0;
}

/**********************************************************/
/**
 * @brief 返回文件中总的 PCM 数据字节数（等于 -1 时，表示无效）。
 */
x_fsize_t fwave_rpcm_t::size(void) const
{
    if (!is_open())
    {
        return -1;
    }

    return m_xst_alen;
}

/**********************************************************/
/**
 * @brief 返回当前读取音频数据的偏移位置（等于 -1 时，表示无效）。
 */
x_fsize_t fwave_rpcm_t::offset(void) const
{
    if (!is_open())
    {
        return -1;
    }

    x_fsize_t xst_vpos = ftell(XHF(m_xht_file));
    if ((xst_vpos < m_xst_abeg) || (xst_vpos >= (m_xst_abeg + m_xst_alen)))
    {
        return -1;
    }

    return (xst_vpos - m_xst_abeg);
}

/**********************************************************/
/**
 * @brief 设置当前读取音频数据的偏移位置。
 * 
 * @param [in ] xst_apos : 偏移位置（为 -1 时，则偏移到文件末尾）。
 * 
 * @return x_int32_t
 *         - 返回  0，表示操作成功；
 *         - 返回 -1，表示操作失败。
 */
x_int32_t fwave_rpcm_t::seek(x_fsize_t xst_apos)
{
    if (!is_open())
    {
        return -1;
    }

    if ((-1 == xst_apos) || (xst_apos > m_xst_alen))
    {
        xst_apos = m_xst_abeg + m_xst_alen;
    }
    else
    {
        xst_apos += m_xst_abeg;
    }

    return fseek(XHF(m_xht_file), xst_apos, SEEK_SET);
}

/**********************************************************/
/**
 * @brief 读取 PCM 数据。
 * 
 * @param [out] xct_dptr : 读取 PCM 数据所使用的缓存。
 * @param [in ] xst_size : 所使用缓存的大小。
 * 
 * @return x_fsize_t
 *         - 成功，返回 读取到的字节数（>= 0）。
 */
x_fsize_t fwave_rpcm_t::read(x_void_t * xct_dptr, x_fsize_t xst_size)
{
    x_fsize_t xst_vpos = 0;

    if (!is_open() || (X_NULL == xct_dptr) || (xst_size <= 0))
    {
        return 0;
    }

    xst_vpos = ftell(XHF(m_xht_file));
    if (xst_vpos >= (m_xst_abeg + m_xst_alen))
    {
        return 0;
    }

    if ((xst_vpos + xst_size) >= (m_xst_abeg + m_xst_alen))
    {
        xst_size = (m_xst_abeg + m_xst_alen) - xst_vpos;
    }

    return (x_fsize_t)fread(
                        xct_dptr,
                        sizeof(x_byte_t),
                        xst_size,
                        XHF(m_xht_file));
}

/**********************************************************/
/**
 * @brief 判断当前是否已经读到音频数据末尾。
 */
x_bool_t fwave_rpcm_t::is_eof(void) const
{
    if (!is_open())
    {
        return X_TRUE;
    }

    if (ftell(XHF(m_xht_file)) >= (m_xst_abeg + m_xst_alen))
    {
        return X_TRUE;
    }

    return X_FALSE;
}

////////////////////////////////////////////////////////////////////////////////
// fwave_wpcm_t

//====================================================================

// 
// fwave_wpcm_t : constructor/destructor
// 

fwave_wpcm_t::fwave_wpcm_t(void)
    : m_xht_file(X_NULL)
{
    memset(&m_fmt_info, 0, sizeof(x_format_t));
}

fwave_wpcm_t::~fwave_wpcm_t(void)
{
    close();
}

//====================================================================

// 
// fwave_wpcm_t : public interfaces
// 

/**********************************************************/
/**
 * @brief 打开文件。
 * 
 * @param [in ] xszt_filename : 目标操作的文件路径名。
 * @param [in ] xut_samprate  : 采样率。
 * @param [in ] xut_sampbits  : 每个采样点的位数。
 * @param [in ] xut_channels  : 通道数。
 * 
 * @return x_errno_t : 错误码。
 * @retval X_ERR_OK, 操作成功。
 * @retval XERR_HINO(xerr_no)，参看 xerrno_table_t 相关枚举值。
 * @retval XERR_LONO(xerr_no)，所引用的模块返回的错误码值。
 */
x_errno_t fwave_wpcm_t::open(
                x_cstring_t xszt_filename,
                x_uint32_t xut_samprate,
                x_uint16_t xut_sampbits,
                x_uint16_t xut_channels)
{
    x_errno_t xerr_no = X_ERR_UNKNOW;

    do
    {
        //======================================
        // 保证参数的有效性

        if ((X_NULL == xszt_filename) ||
            (xut_samprate <= 0)       ||
            (xut_sampbits <= 0)       ||
            (xut_channels <= 0)       ||
            (0 != (xut_sampbits % 8)))
        {
            xerr_no = XERR_MAKE(X_ERR_BAD_ARGS, 0);
            break;
        }

        //======================================
        // 打开文件

        if (is_open())
        {
            close();
        }

        m_xht_file = (x_handle_t)fopen(xszt_filename, "wb+");
        if (X_NULL == m_xht_file)
        {
            xerr_no = XERR_MAKE(X_ERR_FOPEN_FAILED, errno);
            break;
        }

        //======================================
        // 构建 文件头 + 格式信息 + 数据段信息

        x_uchar_t xct_buffer[X_WAVE_MINSIZE] = { 0 };
        x_int32_t xit_offset = 0;

        //======================================
        // 文件头

        x_wave_node_t * xnode0  = (x_wave_node_t *)(xct_buffer + xit_offset);
        xnode0->xct_id[0] = 'R';
        xnode0->xct_id[1] = 'I';
        xnode0->xct_id[2] = 'F';
        xnode0->xct_id[3] = 'F';
        xnode0->xut_size  =  0 ;

        xit_offset += X_WAVE_NDSIZE;

        x_wave_header_t * xheader = (x_wave_header_t *)(xct_buffer + xit_offset);
        xheader->xct_format[0] = 'W';
        xheader->xct_format[1] = 'A';
        xheader->xct_format[2] = 'V';
        xheader->xct_format[3] = 'E';

        xit_offset += X_WAVE_HDSIZE;

        //======================================
        // 格式信息

        x_wave_node_t * xnode1  = (x_wave_node_t *)(xct_buffer + xit_offset);
        xnode1->xct_id[0] = 'f';
        xnode1->xct_id[1] = 'm';
        xnode1->xct_id[2] = 't';
        xnode1->xct_id[3] = ' ';
        xnode1->xut_size  = X_IS_BIG_ENDIAN ? cvt32_order(X_WAVE_FMTSIZE) : X_WAVE_FMTSIZE;

        xit_offset += X_WAVE_NDSIZE;

        x_wave_format_t * xformat = (x_wave_format_t *)(xct_buffer + xit_offset);
        if (X_IS_BIG_ENDIAN)
        {
            xformat->xut_format   = cvt16_order(1);
            xformat->xut_channels = cvt16_order(xut_channels);
            xformat->xut_samprate = cvt32_order(xut_samprate);
            xformat->xut_avgbps   = cvt32_order(xut_samprate * xut_channels * xut_sampbits / 8);
            xformat->xut_blkalign = cvt16_order(xut_channels * xut_sampbits / 8);
            xformat->xut_sampbits = cvt16_order(xut_sampbits);
        }
        else
        {
            xformat->xut_format   = 1;
            xformat->xut_channels = xut_channels;
            xformat->xut_samprate = xut_samprate;
            xformat->xut_avgbps   = xut_samprate * xut_channels * xut_sampbits / 8;
            xformat->xut_blkalign = xut_channels * xut_sampbits / 8;
            xformat->xut_sampbits = xut_sampbits;
        }

        xit_offset += X_WAVE_FMTSIZE;

        //======================================
        // 数据段信息

        x_wave_node_t * xnode2  = (x_wave_node_t   *)(xct_buffer + xit_offset);

        xnode2->xct_id[0] = 'd';
        xnode2->xct_id[1] = 'a';
        xnode2->xct_id[2] = 't';
        xnode2->xct_id[3] = 'a';
        xnode2->xut_size  =  0 ;

        //======================================

        x_size_t xst_wlen = fwrite(
                                xct_buffer,
                                sizeof(x_byte_t),
                                X_WAVE_MINSIZE,
                                XHF(m_xht_file));
        if (X_WAVE_MINSIZE != xst_wlen)
        {
            xerr_no = XERR_MAKE(X_ERR_FWRITE_INFO, errno);
            break;
        }

        m_fmt_info.xut_samprate = xformat->xut_samprate;
        m_fmt_info.xut_sampbits = xformat->xut_sampbits;
        m_fmt_info.xut_channels = xformat->xut_channels;

        //======================================
        xerr_no = 0;
    } while (0);

    if (0 != xerr_no)
    {
        close();
    }

    return xerr_no;
}

/**********************************************************/
/**
 * @brief 关闭文件。
 */
x_void_t fwave_wpcm_t::close(void)
{
    if (X_NULL != m_xht_file)
    {
        fseek(XHF(m_xht_file), 0, SEEK_END);
        x_size_t xst_size = (x_size_t)ftell(XHF(m_xht_file));

        if (xst_size > X_WAVE_MINSIZE)
        {
            x_uint32_t xut_vsize = 0;

            // 修正 x_wave_header_t.xut_size 值
            // 即：存储文件的字节数
            xut_vsize = (x_uint32_t)(xst_size - 8);
            if (X_IS_BIG_ENDIAN)
                xut_vsize = cvt32_order(xut_vsize);
            fseek(XHF(m_xht_file), sizeof(x_uint32_t), SEEK_SET);
            fwrite(&xut_vsize, sizeof(x_uint32_t), 1, XHF(m_xht_file));

            // 修正 x_wave_data_t.xut_sub_2size 值
            // 即：PCM 数据部分的字节数
            xut_vsize = (x_uint32_t)(xst_size - X_WAVE_MINSIZE);
            if (X_IS_BIG_ENDIAN)
                xut_vsize = cvt32_order(xut_vsize);
            fseek(
                XHF(m_xht_file),
                X_WAVE_MINSIZE - sizeof(x_uint32_t),
                SEEK_SET);
            fwrite(&xut_vsize, sizeof(x_uint32_t), 1, XHF(m_xht_file));
        }

        fclose(XHF(m_xht_file));
        m_xht_file = X_NULL;
    }

    memset(&m_fmt_info, 0, sizeof(x_format_t));
}

/**********************************************************/
/**
 * @brief 返回当前文件中总的 PCM 数据字节数（等于 -1 时，表示无效）。
 */
x_fsize_t fwave_wpcm_t::size(void) const
{
    if (!is_open())
    {
        return -1;
    }

    x_fsize_t xst_vpos = ftell(XHF(m_xht_file));
    fseek(XHF(m_xht_file), 0, SEEK_END);

    x_fsize_t xst_size = ftell(XHF(m_xht_file));
    fseek(XHF(m_xht_file), xst_vpos, SEEK_SET);

    XASSERT(xst_size >= X_WAVE_MINSIZE);

    return (xst_size - (x_fsize_t)X_WAVE_MINSIZE);
}

/**********************************************************/
/**
 * @brief 返回当前写入音频数据的偏移位置（等于 -1 时，表示无效）。
 */
x_fsize_t fwave_wpcm_t::offset(void) const
{
    if (!is_open())
    {
        return -1;
    }

    x_fsize_t xst_vpos = ftell(XHF(m_xht_file));
    XASSERT(xst_vpos >= X_WAVE_MINSIZE);

    return (xst_vpos - (x_fsize_t)X_WAVE_MINSIZE);
}

/**********************************************************/
/**
 * @brief 设置当前写入音频数据的偏移位置。
 * 
 * @param [in ] xst_apos : 偏移位置（为 -1 时，则偏移到文件末尾）。
 * 
 * @return x_int32_t
 *         - 返回  0，表示操作成功；
 *         - 返回 -1，表示操作失败。
 */
x_int32_t fwave_wpcm_t::seek(x_fsize_t xst_apos)
{
    if (!is_open())
    {
        return -1;
    }

    return fseek(
        XHF(m_xht_file), 
        (-1 != xst_apos) ? (xst_apos + X_WAVE_MINSIZE) : -1,
        SEEK_SET);
}

/**********************************************************/
/**
 * @brief 写入 PCM 数据。
 * 
 * @param [in ] xct_dptr : PCM 数据缓存。
 * @param [in ] xst_size : 缓存中有效数据的大小。
 * 
 * @return x_fsize_t
 *         - 成功，返回 写入到的字节数（>= 0）；
 *         - 失败，返回 -1。
 */
x_fsize_t fwave_wpcm_t::write(x_void_t * xct_dptr, x_fsize_t xst_size)
{
    if (!is_open())
    {
        return -1;
    }

    if ((X_NULL == xct_dptr) || (xst_size <= 0))
    {
        return 0;
    }

    return (x_fsize_t)fwrite(
                        xct_dptr,
                        sizeof(x_byte_t),
                        xst_size,
                        XHF(m_xht_file));
}
